package cn.funnymap.lgis.tools.crs;

import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import java.util.Arrays;
import java.util.List;

/**
 * 空间参考工具类
 *
 * @author jiaoxn
 */
public class CRSUtil {
    private CRSUtil() {}

    /**
     * 验证空间参考是否有效
     *
     * @param crsWkid 空间参考的WKID
     */
    public static CoordinateReferenceSystem isValid(Integer crsWkid) {
        try {
            return CRS.decode(String.format("EPSG:%s",
                    crsWkid.toString()));
        } catch (FactoryException ignored) {
            throw new RuntimeException("当前不是一个有效的空间参考");
        }
    }

    public static Geometry transform(Geometry geometry, CoordinateReferenceSystem originalCrs,
                                     CoordinateReferenceSystem targetCrs) {
        try {
            if (CRS.getAxisOrder(originalCrs) != CRS.AxisOrder.EAST_NORTH) {
                int originalCrsEpsgCode = getEpsgCode(originalCrs);
                originalCrs = GeographicCRS.getByEpsgCode(originalCrsEpsgCode);
            }
            MathTransform transform = CRS.findMathTransform(originalCrs, targetCrs,true);
            return JTS.transform(geometry, transform);
        } catch (FactoryException factoryException) {
            throw new RuntimeException("空间投影变换定义失败：" + factoryException);
        } catch (TransformException transformException) {
            throw new RuntimeException("空间投影变换失败：" + transformException);
        }
    }

    public static boolean equals(CoordinateReferenceSystem crs1, CoordinateReferenceSystem crs2) {
        try {
            int crs1EpsgCode = getEpsgCode(crs1);
            int crs2EpsgCode = getEpsgCode(crs2);

            return crs1EpsgCode == crs2EpsgCode;
        } catch (FactoryException factoryException) {
            return false;
        }
    }
    private static int getEpsgCode(CoordinateReferenceSystem crs) throws FactoryException {
        return CRS.lookupEpsgCode(crs, true);
    }

    public static List<Double> transform(Double lon, Double lat, CoordinateReferenceSystem originalCrs,
                                         CoordinateReferenceSystem targetCrs) {
        try {
            Coordinate coordinate = new Coordinate(lat, lon);
            GeometryFactory geoFactory = JTSFactoryFinder.getGeometryFactory();
            Point sourcePoint = geoFactory.createPoint(coordinate);
            MathTransform transform = CRS.findMathTransform(originalCrs, targetCrs);
            Coordinate coordinateAfterTransformed = JTS.transform(sourcePoint, transform).getCoordinate();
            return Arrays.asList(coordinateAfterTransformed.getX(), coordinateAfterTransformed.getY());
        } catch (FactoryException factoryException) {
            throw new RuntimeException("空间投影变换定义失败：" + factoryException);
        } catch (TransformException transformException) {
            throw new RuntimeException("空间投影变换失败：" + transformException);
        }
    }
}
